namespace Velocity4Net.Runtime.Directives
{
    using System;
    using MethodInvocationException = Velocity4Net.Errors.MethodInvocationException;
    using ParseErrorException = Velocity4Net.Errors.ParseErrorException;
    using ResourceNotFoundException = Velocity4Net.Errors.ResourceNotFoundException;
    using InternalContextAdapter = Velocity4Net.Ctx.InternalContextAdapter;
    using INode = Velocity4Net.Runtime.Parse.AST.INode;

    /// <summary> Base class for all directives used in Velocity.
    /// *
    /// </summary>
    /// <author> <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
    /// </author>
    /// <version> $Id: Directive.cs,v 1.3 2003/10/27 13:54:10 corts Exp $
    ///
    /// </version>
    public abstract class Directive : DirectiveConstants  //,System.ICloneable
    {
        private String templateName;

        public abstract System.String Name
        {
            get
                           ;
            set; //removed in new version
        }
        /**
    * @return The template file name this directive was defined in, or null if not 
    * defined in a file.
    */
        public String TemplateName
        {
            get
            {
                return templateName;
            }
        }
        /**
     * @return true if there will be a scope control injected into the context
     * when rendering this directive.
     */
        public virtual bool ScopeProvided
        {
            get
            {
                return provideScope;
            }
        }

        /**
     * @returns the name to be used when a scope control is provided for this
     * directive.
     */
        public virtual String ScopeName
        {
            get
            {
                return Name;
            }
        }

        /**
   * This creates and places the scope control for this directive
   * into the context (if scope provision is turned on).
   */
        protected void preRender(InternalContextAdapter context)
        {
            if (ScopeProvided)
            {
                String name = ScopeName;
                Object previous = context.Get(name);
                context.Put(name, makeScope(previous));
            }
        }

        protected Scope makeScope(Object prev)
        {
            return new Scope(this, prev);
        }

        /**
    * This cleans up any scope control for this directive after rendering,
    * assuming the scope control was turned on.
    */
        protected void postRender(InternalContextAdapter context)
        {
            if (ScopeProvided)
            {
                String name = ScopeName;
                Object obj = context.Get(name);

                try
                {
                    Scope scope = (Scope)obj;
                    if (scope.Parent != null)
                    {
                        context.Put(name, scope.Parent);
                    }
                    else if (scope.Replaced != null)
                    {
                        context.Put(name, scope.Replaced);
                    }
                    else
                    {
                        context.Remove(name);
                    }
                }
                catch (System.Exception cce)
                {
                    // the user can override the scope with a #set,
                    // since that means they don't care about a replaced value
                    // and obviously aren't too keen on their scope control,
                    // and especially since #set is meant to be handled globally,
                    // we'll assume they know what they're doing and not worry
                    // about replacing anything superseded by this directive's scope
                }
            }
        }
        public abstract int Type
        {
            get
                     ;
        }
        public virtual int Line
        {
            get
            {
                return line;
            }

        }
        public virtual int Column
        {
            get
            {
                return column;
            }

        }
        private int line = 0;
        private int column = 0;

        protected internal RuntimeServices rsvc = null;
        private bool provideScope;

        /// <summary>Return the name of this directive
        /// </summary>

        /// <summary>Get the directive type BLOCK/LINE
        /// </summary>

        /// <summary>Allows the template location to be set
        /// </summary>
        public virtual void setLocation(int line, int column)
        {
            this.line = line;
            this.column = column;
        }

        /**
     * Allows the template location to be set.
     * @param line
     * @param column
     */
        public void setLocation(int line, int column, String templateName)
        {
            setLocation(line, column);
            this.templateName = templateName;
        }

        /// <summary>for log msg purposes
        /// </summary>

        /// <summary>for log msg purposes
        /// </summary>

        /// <summary> How this directive is to be initialized.
        /// </summary>
        public virtual void Init(RuntimeServices rs, InternalContextAdapter context, INode node)
        {
            rsvc = rs;

            String property = ScopeName + '.' + RuntimeConstants.PROVIDE_SCOPE_CONTROL;
            this.provideScope = rsvc.GetBoolean(property, provideScope);
        }

        /// <summary> How this directive is to be rendered
        /// </summary>
        public abstract bool Render(InternalContextAdapter context, System.IO.TextWriter writer, INode node);
    }
}
